import os
import oss2
from datetime import datetime
from flask import current_app
from app.models.uploaded_file import UploadedFile
from app import db

class OSSService:
    """阿里云OSS服务类"""
    
    def __init__(self):
        self.endpoint = current_app.config['OSS_ENDPOINT']
        self.access_key_id = current_app.config['OSS_ACCESS_KEY_ID']
        self.access_key_secret = current_app.config['OSS_ACCESS_KEY_SECRET']
        self.bucket_name = current_app.config['OSS_BUCKET_NAME']
        self.public_base_url = current_app.config.get('PUBLIC_BASE_URL') or ''
        
        # 允许在本地开发环境缺省 OSS 配置，走本地存储回退
        self.local_fallback = not all([
            self.endpoint, self.access_key_id, self.access_key_secret, self.bucket_name
        ])
        if not self.local_fallback:
            # 初始化OSS客户端
            auth = oss2.Auth(self.access_key_id, self.access_key_secret)
            self.bucket = oss2.Bucket(auth, self.endpoint, self.bucket_name)
    
    def upload_file(self, file_path, original_filename, decoration_request_id=None):
        """
        上传文件到OSS
        
        Args:
            file_path: 本地文件路径
            original_filename: 原始文件名
            decoration_request_id: 关联的装修请求ID
            
        Returns:
            UploadedFile对象
        """
        try:
            # 生成唯一的文件名
            timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
            file_extension = os.path.splitext(original_filename)[1]
            unique_filename = f"floor_plans/{timestamp}_{original_filename}"

            # 获取文件大小
            file_size = os.path.getsize(file_path)

            if self.local_fallback:
                # 本地存储回退
                # 统一使用项目根下的 uploads 绝对路径，避免相对路径导致找不到文件
                project_root = os.path.abspath(os.path.join(current_app.root_path, '..'))
                uploads_dir = os.path.join(project_root, 'uploads')
                os.makedirs(os.path.join(uploads_dir, 'floor_plans'), exist_ok=True)
                local_dest = os.path.join(uploads_dir, unique_filename.replace('/', os.sep))
                # 使用复制再删除的方式，避免 os.replace 后控制器层再删除失败
                import shutil
                shutil.copy2(file_path, local_dest)
                # 返回可被外部访问的绝对URL（供云端工作流访问）
                if self.public_base_url:
                    base = self.public_base_url.rstrip('/')
                    oss_url = f"{base}/uploads/{unique_filename}"
                else:
                    # 兜底为相对路径（仍可被前端访问，但外部服务可能访问不到）
                    oss_url = f"/uploads/{unique_filename}"
            else:
                # 上传文件到OSS
                result = self.bucket.put_object_from_file(unique_filename, file_path)
                if result.status != 200:
                    raise Exception(f"OSS上传失败，状态码: {result.status}")
                # 生成可访问URL：优先使用带有效期的签名URL，确保私有桶也能被外部服务访问
                try:
                    oss_url = self.bucket.sign_url('GET', unique_filename, 3600)
                except Exception:
                    # 退回公共读直链（要求桶为公共读）
                    oss_url = f"https://{self.bucket_name}.{self.endpoint.replace('https://', '')}/{unique_filename}"

            # 保存到数据库
            uploaded_file = UploadedFile(
                original_filename=original_filename,
                oss_filename=unique_filename,
                oss_url=oss_url,
                file_size=file_size,
                file_type=file_extension.lower(),
                decoration_request_id=decoration_request_id
            )

            db.session.add(uploaded_file)
            db.session.commit()

            return uploaded_file

        except Exception as e:
            db.session.rollback()
            raise Exception(f"文件上传失败: {str(e)}")
    
    def delete_file(self, oss_filename):
        """
        从OSS删除文件
        
        Args:
            oss_filename: OSS中的文件名
        """
        try:
            self.bucket.delete_object(oss_filename)
        except Exception as e:
            raise Exception(f"文件删除失败: {str(e)}")
    
    def get_file_url(self, oss_filename):
        """
        获取文件访问URL
        
        Args:
            oss_filename: OSS中的文件名
            
        Returns:
            文件访问URL
        """
        return f"https://{self.bucket_name}.{self.endpoint.replace('https://', '')}/{oss_filename}"
